<template>
    <div
      class="el-progress"
      :class="[
        'el-progress--' + type,
        status ? 'is-' + status : '',
        {
          'el-progress--without-text': !showText,
          'el-progress--text-inside': textInside,
        }
      ]"
      role="progressbar"
      :aria-valuenow="percentage"
      aria-valuemin="0"
      aria-valuemax="100"
    >
      <div class="el-progress-bar" v-if="type === 'line'">
        <div class="el-progress-bar__outer" :style="{height: strokeWidth + 'px'}">
          <div class="el-progress-bar__inner" :style="barStyle">
            <div class="el-progress-bar__innerText" v-if="showText && textInside">{{content}}</div>
          </div>
        </div>
      </div>
      <div class="el-progress-circle" :style="{height: width + 'px', width: width + 'px'}" v-else>
        <svg viewBox="0 0 100 100">
          <path
            class="el-progress-circle__track"
            :d="trackPath"
            stroke="#e5e9f2"
            :stroke-width="relativeStrokeWidth"
            fill="none"
            :style="trailPathStyle"></path>
          <path
            class="el-progress-circle__path"
            :d="trackPath"
            :stroke="stroke"
            fill="none"
            :stroke-linecap="strokeLinecap"
            :stroke-width="percentage ? relativeStrokeWidth : 0"
            :style="circlePathStyle"></path>
        </svg>
      </div>
      <div
        class="el-progress__text"
        v-if="showText && !textInside"
        :style="{fontSize: progressTextSize + 'px'}"
      >
        <template v-if="!status">{{content}}</template>
        <i v-else :class="iconClass"></i>
      </div>
    </div>
  </template>
  <script>
    export default {
      name: 'ElProgress',
      props: {
        type: {
          type: String,
          default: 'line',
          validator: val => ['line', 'circle', 'dashboard'].indexOf(val) > -1
        },
        percentage: {
          type: Number,
          default: 0,
          required: true,
          validator: val => val >= 0 && val <= 100
        },
        status: {
          type: String,
          validator: val => ['success', 'exception', 'warning'].indexOf(val) > -1
        },
        strokeWidth: {
          type: Number,
          default: 6
        },
        strokeLinecap: {
          type: String,
          default: 'round'
        },
        textInside: {
          type: Boolean,
          default: false
        },
        width: {
          type: Number,
          default: 126
        },
        showText: {
          type: Boolean,
          default: true
        },
        color: {
          type: [String, Array, Function],
          default: ''
        },
        format: Function
      },
      computed: {
        barStyle() {
          const style = {};
          style.width = this.percentage + '%';
          style.backgroundColor = this.getCurrentColor(this.percentage);
          return style;
        },
        relativeStrokeWidth() {
          return (this.strokeWidth / this.width * 100).toFixed(1);
        },
        radius() {
          if (this.type === 'circle' || this.type === 'dashboard') {
            return parseInt(50 - parseFloat(this.relativeStrokeWidth) / 2, 10);
          } else {
            return 0;
          }
        },
        trackPath() {
          const radius = this.radius;
          const isDashboard = this.type === 'dashboard';
          return `
            M 50 50
            m 0 ${isDashboard ? '' : '-'}${radius}
            a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '-' : ''}${radius * 2}
            a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '' : '-'}${radius * 2}
            `;
        },
        perimeter() {
          return 2 * Math.PI * this.radius;
        },
        rate() {
          return this.type === 'dashboard' ? 0.75 : 1;
        },
        strokeDashoffset() {
          const offset = -1 * this.perimeter * (1 - this.rate) / 2;
          return `${offset}px`;
        },
        trailPathStyle() {
          return {
            strokeDasharray: `${(this.perimeter * this.rate)}px, ${this.perimeter}px`,
            strokeDashoffset: this.strokeDashoffset
          };
        },
        circlePathStyle() {
          return {
            strokeDasharray: `${this.perimeter * this.rate * (this.percentage / 100) }px, ${this.perimeter}px`,
            strokeDashoffset: this.strokeDashoffset,
            transition: 'stroke-dasharray 0.6s ease 0s, stroke 0.6s ease'
          };
        },
        stroke() {
          let ret;
          if (this.color) {
            ret = this.getCurrentColor(this.percentage);
          } else {
            switch (this.status) {
              case 'success':
                ret = '#13ce66';
                break;
              case 'exception':
                ret = '#ff4949';
                break;
              case 'warning':
                ret = '#e6a23c';
                break;
              default:
                ret = '#20a0ff';
            }
          }
          return ret;
        },
        iconClass() {
          if (this.status === 'warning') {
            return 'el-icon-warning';
          }
          if (this.type === 'line') {
            return this.status === 'success' ? 'el-icon-circle-check' : 'el-icon-circle-close';
          } else {
            return this.status === 'success' ? 'el-icon-check' : 'el-icon-close';
          }
        },
        progressTextSize() {
          return this.type === 'line'
            ? 12 + this.strokeWidth * 0.4
            : this.width * 0.111111 + 2 ;
        },
        content() {
          if (typeof this.format === 'function') {
            return this.format(this.percentage) || '';
          } else {
            return `${this.percentage}%`;
          }
        }
      },
      methods: {
        getCurrentColor(percentage) {
          if (typeof this.color === 'function') {
            return this.color(percentage);
          } else if (typeof this.color === 'string') {
            return this.color;
          } else {
            return this.getLevelColor(percentage);
          }
        },
        getLevelColor(percentage) {
          const colorArray = this.getColorArray().sort((a, b) => a.percentage - b.percentage);
  
          for (let i = 0; i < colorArray.length; i++) {
            if (colorArray[i].percentage > percentage) {
              return colorArray[i].color;
            }
          }
          return colorArray[colorArray.length - 1].color;
        },
        getColorArray() {
          const color = this.color;
          const span = 100 / color.length;
          return color.map((seriesColor, index) => {
            if (typeof seriesColor === 'string') {
              return {
                color: seriesColor,
                percentage: (index + 1) * span
              };
            }
            return seriesColor;
          });
        }
      }
    };
  </script>
  